/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with 
    Amrita ITEWS if not then it can be viewed here: 
    http://itews.amrita.ac.in/license.html.

    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/

    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews

    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa 
    Vidyapeetham as part of the project titled,"Intelligent & Interactive 
    Telematics using Emerging Wireless Technologies for Transport Systems 
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment 
    Council (TIFAC), India.
***/

package currentroutegrabber;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;
import org.knopflerfish.service.log.LogRef;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;

public class CurrentRouteGrabber extends Thread 
{
	private static final String CURRENT_ROUTE = "currentRoute";
	private static final String NO_ROUTE ="noroute";
	private static final String FAILED ="0\n";
	private static final String SUCCEEDED ="1\n";
	private static final String NEW_MEDIA_SET_ARRIVED = "newMediaSet";
	private EventAdmin eventAdmin;
	private  LogRef log;
	private boolean running = true;
	private ServerSocket serverSocket;
	private Socket clientSocket;
	private CurrentRouteGrabberConfig config;
	private BufferedReader bufferedReader;
	private PrintStream printStream;
	private DBConnectionPool dbConnectionPool ;
	private String mediaPlayerQueueDelete = "DELETE FROM media_player_queue";
	private String persistentDataQuery = "SELECT current_route_code FROM persistent_data LIMIT 1";
	private String lastMediaPlayedDelete = "DELETE FROM last_media_played";
	private String persistentDataUpdate = "UPDATE persistent_data SET current_node_code=?, current_node_seq_no=?, current_route_code=?, current_trigger_descriptor_id=?";
	private String persistentDataInsert = "INSERT INTO persistent_data VALUES(1, ?, default, ?, ?,default, default, default)";
	private String routeSchedulesQuery = "SELECT usage_count FROM route_schedules WHERE route_schedule_code=?";
	private String routeSchedulesUpdate = "UPDATE route_schedules SET is_current_route='T', usage_count=? WHERE route_schedule_code=?";
	private String routeSchedulesGlobalUpdate = "UPDATE route_schedules SET is_current_route='F'";
	
	public CurrentRouteGrabber(EventAdmin eventAdmin) throws Exception
	{
		try
		{
			log = new LogRef(Activator.bc);
			config = CurrentRouteGrabberConfig.getConfig();
			this.eventAdmin = eventAdmin;
			serverSocket = new ServerSocket(config.getRouteCodeListeningPort());
			dbConnectionPool = DBConnectionPool.getInstance(config.getJdbcConnectionURI());
			Connection conn = null;
			PreparedStatement preparedStmt1 = null;
			PreparedStatement preparedStmt2 = null;
			ResultSet rs1 = null;
			try
			{
				dbConnectionPool = DBConnectionPool.getInstance(config.getJdbcConnectionURI());
				conn = dbConnectionPool.getConnection();
				conn.setAutoCommit(false);
				preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, persistentDataQuery);
				rs1 = preparedStmt1.executeQuery();
				if (rs1.next() == false)
				{
					preparedStmt2 = dbConnectionPool.getPreparedStmt(conn, persistentDataInsert);
					preparedStmt2.setString(1, "");
					preparedStmt2.setString(2, NO_ROUTE);
					preparedStmt2.setInt(3, 0);
					preparedStmt2.executeUpdate();
					preparedStmt2.close();
					preparedStmt2 = null;
				}
				rs1.close();
				rs1 = null;
				preparedStmt1.close();
				preparedStmt1 = null;
				conn.commit();
			}
			catch(Exception e)
			{
				e.printStackTrace();
				throw e;
			}
			finally
			{
	    		if (rs1 != null)
	    		{
	    			try {rs1.close();} catch (SQLException e) {} 
	    			rs1 = null;
	    		}
	    		if (preparedStmt1 != null)
	    		{
	    			try {preparedStmt1.close();} catch (SQLException e) {} 
	    			preparedStmt1 = null;
	    		}
	    		if (preparedStmt2 != null)
	    		{
	    			try {preparedStmt2.close();} catch (SQLException e) {} 
	    			preparedStmt2 = null;
	    		}
				if (conn != null)
				{
					try {conn.close();} catch (SQLException e) {}
					conn = null;
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
			throw e;
		}
	}
	public void run() 
	{
		boolean success = false;
		while(running == true)
		{
			try
			{
				success = false;
				clientSocket = serverSocket.accept();
				log.info("Accepted connection from driver console");
				bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
				printStream = new PrintStream(clientSocket.getOutputStream());
				String newRouteCode = bufferedReader.readLine();
				log.info("Received routeCode:****** "+newRouteCode);
				if (newRouteCode != null && !newRouteCode.trim().equals(""))
				{
					success = handleCurrentRouteEvent(newRouteCode);
				}
			}
			catch(SocketException se)
			{
				//log.warn("CurrentRouteGrabber: Socket closed");
				try {Thread.sleep(2000);} catch (InterruptedException e1) {}
			}
			catch(Exception e)
			{
				e.printStackTrace();
				try {Thread.sleep(2000);} catch (InterruptedException e1) {}
			}
			finally
			{
				if (printStream != null)
				{
					if (success == true)
					{
						printStream.print(SUCCEEDED);
					}
					else
					{
						printStream.print(FAILED);
					}
		 			printStream.flush();
				}
				if (bufferedReader != null)
				{
					try {bufferedReader.close();} catch (IOException e) {}
					bufferedReader = null;
				}
				if (printStream != null)
				{
					printStream.close();
					printStream = null;
				}
				if (clientSocket != null)
				{
					try {clientSocket.close();} catch (IOException e) {}
					clientSocket = null;
				}
			}
		}
	}
	
	private synchronized boolean handleCurrentRouteEvent(String newRouteCode)
	{
		boolean status = false;
		Connection conn = null;
		PreparedStatement preparedStmt = null;
		PreparedStatement preparedStmt1 = null;
		PreparedStatement preparedStmt2 = null;
		PreparedStatement preparedStmt2A = null;
		ResultSet rs = null;
		try
		{
			String currentRouteCode = "";
			conn = dbConnectionPool.getConnection();
			preparedStmt = dbConnectionPool.getPreparedStmt(conn, persistentDataQuery);
			rs = preparedStmt.executeQuery();
			if (rs.next() == true)
			{
				currentRouteCode = rs.getString(1).trim();
			}
			rs.close();
			rs = null;
			preparedStmt.close();
			preparedStmt = null;
			if (currentRouteCode != null && !currentRouteCode.equals(newRouteCode))
			{
				int usage_count = 0;
				preparedStmt = dbConnectionPool.getPreparedStmt(conn, routeSchedulesQuery);
				preparedStmt.setString(1, newRouteCode);
				rs = preparedStmt.executeQuery();
				if (rs.next() == true)
				{
					usage_count = rs.getInt(1);
				}
				rs.close();
				rs = null;
				preparedStmt.close();
				preparedStmt = null;
				
				
				preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, routeSchedulesGlobalUpdate);
				preparedStmt1.executeUpdate();
				preparedStmt1.close();
				preparedStmt1 = null;
				
				preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, routeSchedulesUpdate);
				preparedStmt1.setInt(1, ++usage_count);
				preparedStmt1.setString(2, newRouteCode);
				preparedStmt1.executeUpdate();
				preparedStmt1.close();
				preparedStmt1 = null;
				
				preparedStmt1 = dbConnectionPool.getPreparedStmt(conn, persistentDataUpdate);
				preparedStmt1.setString(1, "");
				if (newRouteCode.equals(NO_ROUTE))
				{
					preparedStmt1.setInt(2, -1);
				}
				else
				{
					preparedStmt1.setInt(2, 0);
				}
				preparedStmt1.setString(3, newRouteCode);
				preparedStmt1.setInt(4, 0);
				preparedStmt1.executeUpdate();
				preparedStmt1.close();
				preparedStmt1 = null;
				
				preparedStmt2 = dbConnectionPool.getPreparedStmt(conn, mediaPlayerQueueDelete);
				preparedStmt2.executeUpdate();
				preparedStmt2.close();
				preparedStmt2 = null;
				
				preparedStmt2A = dbConnectionPool.getPreparedStmt(conn, lastMediaPlayedDelete);
				preparedStmt2A.executeUpdate();
				preparedStmt2A.close();
				preparedStmt2A = null;
				
				Hashtable routeInfo = new Hashtable();
				routeInfo.put(CURRENT_ROUTE, newRouteCode.trim());
				Event event = new Event(CURRENT_ROUTE, routeInfo);
				this.eventAdmin.postEvent(event);
				log.info("Published new RouteCode received from driver console: " +newRouteCode);
				
				//Notify Media Player Manager regarding the route change
				Hashtable newMediaSetArrivalInfo = new Hashtable();
				event = new Event(NEW_MEDIA_SET_ARRIVED, newMediaSetArrivalInfo);
				this.eventAdmin.postEvent(event);
			}
			status = true;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
    		if (rs != null)
    		{
    			try {rs.close();} catch (SQLException e) {} 
    			rs = null;
    		}
			if (preparedStmt != null)
    		{
    			try {preparedStmt.close();} catch (SQLException e) {} 
    			preparedStmt = null;
    		}
			if (preparedStmt1 != null)
    		{
    			try {preparedStmt1.close();} catch (SQLException e) {} 
    			preparedStmt1 = null;
    		}
    		if (preparedStmt2 != null)
    		{
    			try {preparedStmt2.close();} catch (SQLException e) {} 
    			preparedStmt2 = null;
    		}
      		if (preparedStmt2A != null)
    		{
    			try {preparedStmt2A.close();} catch (SQLException e) {} 
    			preparedStmt2A = null;
    		}
			if (conn != null)
			{
				try {conn.close();} catch (SQLException e) {}
				conn = null;
			}
		}
		return status;
	}
	
	protected void stopThread()
	{
		running = false;
		if (bufferedReader != null)
		{
			try {bufferedReader.close();} catch (IOException e) {}
			bufferedReader = null;
		}
		if (printStream != null)
		{
			printStream.close();
			printStream = null;
		}
		if (clientSocket != null)
		{
			try {clientSocket.close();} catch (IOException e) {}
			clientSocket = null;
		}
		if (serverSocket != null)
		{
			try {serverSocket.close();} catch (IOException e) {}
			serverSocket = null;
		}
	}
}
